/*
 * Copyright:  2018 smarabbit studio.
 *
 * Licensed under the Confluent Community License; you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 *
 * http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under
 *  the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 *  either express or implied.  See the License for the specific language governing
 *  permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:    2020/8/10 下午1:40
 *
 */
package com.massyframework.beanskin.runtime.service.crypto;

import org.bouncycastle.util.Arrays;

import com.massyframework.beanskin.service.crypto.AESService;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.util.Objects;

/**
 * {@link BCAESService}, 基于bouncycastle的AES加密服务
 */
public class BCAESService implements AESService {

    //算法名
    public static final String KEY_ALGORITHM = "AES";
    //加解密算法/模式/填充方式
    public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";

    public BCAESService() {
    }

    /**
     * 加密
     *
     * @param content {@link byte}数组，要加密的内容
     * @param key     {@link String}, 密钥，长度只能是16字节
     * @return {@link byte}数组, 已加密的内容
     */
    @Override
    public byte[] encrypt(byte[] content, String key) throws Exception {
        Objects.requireNonNull(content, "\"content\" cannot be null.");
        Objects.requireNonNull(key, "\"key\" cannot be null.");
        if (key.length() != 16){
            throw new IllegalArgumentException("key length is not 16 byte.");
        }

        //转化为密钥
        Key keySpec = convertToKey(key.getBytes("utf-8"));

        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        //设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, this.generateIV());
        return cipher.doFinal(content);
    }

    /**
     * 解密
     *
     * @param encryptedData {@link byte}数组，已加密的内容
     * @param key           {@link String}, 密钥，长度只能是16字节
     * @return {@link byte}数组，原始数据
     * @throws Exception
     */
    @Override
    public byte[] decrypt(byte[] encryptedData, String key) throws Exception {
        Objects.requireNonNull(encryptedData, "\"content\" cannot be null.");
        Objects.requireNonNull(key, "\"key\" cannot be null.");
        if (key.length() != 16){
            throw new IllegalArgumentException("key length is not 16 byte.");
        }

        //转化为密钥
        Key keySpec = convertToKey(key.getBytes("utf-8"));
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        //设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, keySpec, this.generateIV());
        return cipher.doFinal(encryptedData);
    }


    /**
     * 转换为java的密钥格式
     * @param keyBytes
     * @return
     * @throws Exception
     */
    public Key convertToKey(byte[] keyBytes) throws Exception{
        SecretKey secretKey = new SecretKeySpec(keyBytes,KEY_ALGORITHM);
        return secretKey;
    }

    /**
     * 生成iv
     * @return {@link AlgorithmParameters}
     * @throws Exception
     */
    private AlgorithmParameters generateIV() throws Exception{
        //iv 为一个 16 字节的数组，这里采用和 iOS 端一样的构造方法，数据全为0
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_ALGORITHM);
        params.init(new IvParameterSpec(iv));
        return params;
    }
}
